package main

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"go-file-perception-model/internal/config"
)

// Tool descriptor for tools/list
func ToolViewFileContentDescriptor() Tool {
	return Tool{
		Name:        "view_file_content",
		Description: "查看文件内容，可指定起始/结束行(1-based)，返回完整文本内容，每行起始位置携带行号",
		InputSchema: map[string]any{
			"type": "object",
			"properties": map[string]any{
				"file_path":  map[string]any{"type": "string", "description": "文件路径，支持相对路径"},
				"start_line": map[string]any{"type": "integer", "default": 1, "description": "起始行(从1开始)"},
				"end_line":   map[string]any{"type": "integer", "description": "结束行(包含)"},
			},
			"required": []string{"file_path"},
		},
	}
}

// resolveFilePath resolves relative file paths against configured index directories.
// If the input is absolute, it is returned as-is. If relative, it tries each
// configured index directory; if none match, it falls back to current working directory.
func resolveFilePath(input string) (string, error) {
	if input == "" {
		return "", fmt.Errorf("empty file path")
	}
	if filepath.IsAbs(input) {
		return input, nil
	}
	cfg, err := config.LoadConfig()
	if err == nil && len(cfg.FileIndex.IndexDirectories) > 0 {
		for _, base := range cfg.FileIndex.IndexDirectories {
			cand := filepath.Join(base, input)
			if _, statErr := os.Stat(cand); statErr == nil {
				return cand, nil
			}
		}
		// even if not found, prefer the first index dir as base
		return filepath.Abs(filepath.Join(cfg.FileIndex.IndexDirectories[0], input))
	}
	// fallback to CWD
	cwd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	return filepath.Abs(filepath.Join(cwd, input))
}

// Non-stream call handler
func CallViewFileContent(args map[string]any) (any, error) {
	filePath, _ := args["file_path"].(string)
	if filePath == "" {
		return nil, fmt.Errorf("file_path required")
	}
	// resolve relative path using configured index directories
	resolved, err := resolveFilePath(filePath)
	if err != nil {
		return nil, err
	}
	start := 1
	if v, ok := args["start_line"].(float64); ok {
		start = int(v)
	}
	end := -1
	if v, ok := args["end_line"].(float64); ok {
		end = int(v)
	}
	return readFileRange(resolved, start, end)
}

// Stream call handler
func StreamViewFileContent(ctx context.Context, args map[string]any, enc *json.Encoder, flusher http.Flusher) error {
	filePath, _ := args["file_path"].(string)
	if filePath == "" {
		return fmt.Errorf("file_path required")
	}
	resolved, err := resolveFilePath(filePath)
	if err != nil {
		return err
	}
	start := 1
	if v, ok := args["start_line"].(float64); ok {
		start = int(v)
	}
	end := -1
	if v, ok := args["end_line"].(float64); ok {
		end = int(v)
	}

	f, err := os.Open(resolved)
	if err != nil {
		return err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	lineNo := 0
	sent := 0
	var fullText strings.Builder
	lineContents := make([]map[string]any, 0) // 用于metadata中的{line: lineNumber, content: lineContent}格式
	
	for scanner.Scan() {
		lineNo++
		if lineNo < start {
			continue
		}
		if end > 0 && lineNo > end {
			break
		}
		
		// 添加到完整文本
		fullText.WriteString(fmt.Sprintf("%d: %s\n", lineNo, scanner.Text()))
		// 添加到行内容数组
		lineContents = append(lineContents, map[string]any{
			"line":    lineNo,
			"content": scanner.Text(),
		})
		sent++
	}
	
	if err := scanner.Err(); err != nil {
		return err
	}
	
	var rangeStr string
	if end > 0 {
		rangeStr = fmt.Sprintf("%d-%d", start, end)
	} else {
		rangeStr = fmt.Sprintf("%d-EOF", start)
	}
	
	// 创建符合MCP标准的内容对象
	contentObj := map[string]any{
		"type": "text",
		"text": fullText.String(),
		"metadata": map[string]any{
			"range":       rangeStr,
			"lineContents": lineContents, // [lineNumber, content]格式
		},
	}
	
	_ = enc.Encode(map[string]any{"event": "item", "data": contentObj})
	flusher.Flush()
	
	_ = enc.Encode(map[string]any{"event": "item", "data": map[string]any{"summary": map[string]any{"lines": sent, "range": rangeStr}}})
	flusher.Flush()
	return nil
}

// Helper: read file range and return content with statistics
func readFileRange(path string, start, end int) (map[string]any, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	lineNo := 0
	collected := make([]string, 0)
	for scanner.Scan() {
		lineNo++
		if lineNo < start {
			continue
		}
		if end > 0 && lineNo > end {
			break
		}
		collected = append(collected, scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	rangeStr := ""
	if end > 0 {
		rangeStr = fmt.Sprintf("%d-%d", start, end)
	} else {
		rangeStr = fmt.Sprintf("%d-EOF", start)
	}
	
	// 构建完整文本内容，每行起始位置携带行号
	var fullText strings.Builder
	lineContents := make([]map[string]any, 0) // 用于metadata中的{line: lineNumber, content: lineContent}格式
	
	for i, line := range collected {
		lineNumber := start + i
		// 添加到完整文本
		fullText.WriteString(fmt.Sprintf("%d: %s\n", lineNumber, line))
		// 添加到metadata中的行内容数组
		lineContents = append(lineContents, map[string]any{
			"line":    lineNumber,
			"content": line,
		})
	}
	
	// 创建符合MCP标准的内容对象
	contentObj := map[string]any{
		"type": "text",
		"text": fullText.String(),
		"metadata": map[string]any{
			"range":       rangeStr,
			"lineContents": lineContents, // [lineNumber, content]格式
		},
	}
	
	return map[string]any{
		"content": []map[string]any{contentObj},
		"lines":   len(collected),
		"range":   rangeStr,
	}, nil
}

func joinLines(lines []string) string {
	if len(lines) == 0 {
		return ""
	}
	// manual join to avoid importing strings just for this
	b := make([]byte, 0, 1024)
	for i, s := range lines {
		if i > 0 {
			b = append(b, '\n')
		}
		b = append(b, []byte(s)...)
	}
	return string(b)
}
